/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef RENDER_MNG_HH
#define RENDER_MNG_HH
//! \addtogroup introsight_framework
//@{

// INCLUDES
#include "util.hh"
#include "refcnt.hh"
#include "introsight.hh"

// TYPES
enum render_pass
{
    RENDER_PASS_PRECALC=0,    // initialize render results and matrices
    RENDER_PASS_OPAQUE,
    RENDER_PASS_BLEND,
    RENDER_PASS_HUD,
    RENDER_PASS_LAST,
    RENDER_PASS_SELECT,
    RENDER_PASS_LAST_ALL,
};

enum render_fonts
{
    RENDER_FONT_BITMAP=0,
    RENDER_FONT_FT_DEFAULT,
};

extern const char *render_pass_name[RENDER_PASS_LAST_ALL];

class ogl_window;
class ogl_font;
class render_obj;
class render_obj_frame;
class render_obj_camera;
class render_params;
class render_stats;
class time_queue;
class event_mng;

class render_mng: public reference_counter
{
    // render manager configuration
    bool refresh_full;
    unsigned refresh_min_time; // only if refresh_full == false

    // window to draw to
    ogl_window *w;

    // fonts
    ogl_font **f;
    int n_f, max_f;

    // events
    event_mng *event;
    unsigned event_last;           // if 0, see event_mng::event_last; else IS_STATUS_* xor bit changes

    // render hierarchy root
    render_obj *root;

    // viewport in windows coordinates (set by frames to be used by their children)
    render_obj_frame *obj_frame;

    // camera parameters (set by cameras to be used by their children)
    render_obj_camera *obj_camera;

    // render hierarchy (does not increase reference counts)
    render_obj **hierarchy;
    int n_hierarchy, max_hierarchy;

    // event time queue
    time_queue *queue;

    // complete render required; this is set at configure events
    bool forced_render;

    // current pass of the render
    render_pass pass;
    bool pass_hud_aux;   // true if auxiliary buffer (GL_AUX0) is used for HUD pass

    // selection: OpenGL GL_SELECT mode
    unsigned *select_gl_buffer;
    int n_select_gl_buffer, i_select_gl_buffer;
    render_obj **select_hierarchy;
    int n_select_hierarchy, max_select_hierarchy;
    double select_depth_top;

    // calculations
    union
    {
	unsigned calc_all;
	struct
	{
	    unsigned calc_modelview:1;
	    unsigned calc_modelview_nocam:1;
	    unsigned calc_projection:1;
	    unsigned calc_inv_modelview:1;
	    unsigned calc_inv_projection:1;
	    unsigned calc_inv_utils:1;
	};
    };
    double calc_modelview_matrix[IS_MAT4X4];
    double calc_modelview_nocam_matrix[IS_MAT4X4];
    double calc_projection_matrix[IS_MAT4X4];
    double calc_inv_modelview_matrix[IS_MAT4X4];
    double calc_inv_projection_matrix[IS_MAT4X4];
    double calc_inv_utils_data[4];

    // action focus
    unsigned focus_action;
    render_obj *focus_obj;
    int focus_coord[IS_2D];     // in-focus coordinates
    int focus_coord_bak[IS_2D]; // in-focus coordinates backup

    // action params
    void *action_params; // for render object generated actions
    int max_action_params;

    // render params (render quality properties)
    render_params *params;

    // statistics (this stats object is set by render_obj_frame when rendering)
    render_stats *stats_current;

    // PRIVATE METHODS
    void ctor();
    void dtor();

    // selection
    void select_render();
    void select_closer();
    void select_add_selection_mark(int from, int to_not_included);
    void select_remove_selection_mark(int from, int to_not_included);
    void select_check_selection_mark(int i);

    // actions
    bool action_user_dispatch(int i_hierarchy, unsigned action);

public:
    render_mng();
    virtual ~render_mng();

    bool init(ogl_window *win, event_mng *event);

    // return the number of usec for the next render
    int render();

    // update front framebuffer from back one
    bool render_update_from_back(int *ogl_window_coord=NULL); // if NULL, complete window

    // maintain hierarchy
    void hierarchy_enter(render_obj *ro);
    void hierarchy_leave();

    // set forced pass hierarchically up to obj at pass p
    void set_forced_pass(render_pass p, render_obj *obj);

    // events
    static void event_callback_static(render_mng *obj);
    void event_callback();
    inline event_mng *get_event() {return event;}
    inline unsigned get_event_last() {return event_last;}
    void event_action_from_render_obj(unsigned action,
				      unsigned obj_type,
				      unsigned obj_status); // actions triggered from render objs while in render

    // stats
    inline render_stats *get_stats() {return stats_current;} // returns a borrowed reference
    void set_stats(render_stats *stats); // gets reference of provided obj releasing previous obj

    // selection
    inline int selection_n_hierarchy() {return n_select_hierarchy;}
    inline render_obj *selection_hierarchy(int i) {return select_hierarchy[i];}
    // returns n_params and point params into parameter list
    // params are gl_names with (param & IS_GL_NAME_PARAM ) == true (includes selectors)
    int selection_obj_params(render_obj *ro, unsigned **params);
    // returns top object in selection
    inline render_obj *get_select_hierarchy_top() {return n_select_hierarchy?select_hierarchy[n_select_hierarchy-1]:NULL;}
    // returns top object selector (selectors are gl_names with (param & IS_GL_NAME_PARAM) == 0x1 == IS_GL_NAME_SELECTOR
    unsigned get_select_hierarchy_top_selector();
    double get_select_depth_top() {return select_depth_top;} // [-1, 1] ogl coords
    void get_selection_touch_point(double v[IS_3D]);

    // calculations
    double *calculate_selection_modelview_matrix();
    double *calculate_selection_modelview_nocam_matrix();
    double *calculate_selection_projection_matrix();
    double *calculate_selection_inv_modelview_matrix();
    double *calculate_selection_inv_projection_matrix();
    double *calculate_selection_inv_utils();
    double *calculate_hierarchy_modelview_nocam_matrix(double m[IS_MAT4X4]); // this is not cached

    // focus
    inline unsigned get_focus_action() {return focus_action;}
    inline int get_focus_coord(int i_coord) {return focus_coord[i_coord];}
    inline render_obj *get_focus_obj() {return focus_obj;}

    // action params
    void *get_action_params(int size=0);

    // getters
    inline render_pass get_pass() {return pass;}
    inline unsigned get_pass_mask() {return 1<<pass;}
    inline ogl_window *get_window() {return w;}
    inline render_obj_frame *get_obj_frame() {return obj_frame;}
    inline render_obj_camera *get_obj_camera() {return obj_camera;}
    //inline int get_viewport(int i_coord) {return viewport[i_coord];}
    //inline int *get_viewport() {return viewport;}
    //inline float get_viewport_ratio() {return (double)viewport[IS_WW]/(double)viewport[IS_WH];}
    inline bool get_forced_render() {return forced_render;}
    inline time_queue *get_queue() {return queue;}
    inline render_obj *get_root() {return root;}
    inline ogl_font *get_font(render_fonts i) {return f[i];}
    inline bool get_pass_hud_aux() {return pass_hud_aux;}

    // setters
    inline void set_pass(render_pass x) {pass=x;}
    inline void inc_pass() {pass=(render_pass)((int)pass+1);}
    inline void set_obj_frame(render_obj_frame *frame) {obj_frame=frame;}
    inline void set_obj_camera(render_obj_camera *camera) {obj_camera=camera;}
    inline void set_forced_render(bool set) {forced_render=set;}
};

//@}
#endif

/* Offline metadata attributes:

   @metadata_attrs(
	render_mng
	{
		# members
		refresh_full, 
		refresh_min_time:
						select
		w, root, params, queue:
						select, read_only
		event:
						select, read_only, nopickle, private

		# methods
		init, render:
						select
	}
   )
*/
